{-# LANGUAGE FlexibleContexts , BangPatterns #-}
module Perceptron.Vector  
    ( I(..)
    , Global
    , Local(..)
    , Weights
    , WeightsST
    , toSV
    , for_
    , plus_
    , minus_
    , plus
    , minus
    , scale
    , dot 
    , dot'
    )
where

import Data.Array.ST
import Data.Array.Unboxed
import Control.Monad.ST
import Data.STRef
import Control.Monad
import qualified Data.Map as Map
import Data.List (foldl',sort)
import qualified Data.Vector.Unboxed as V
import Data.Binary

data I = I {-# UNPACK #-} !Int {-# UNPACK #-} !Int deriving (Eq,Ord,Ix,Show)
instance Binary I where 
    put (I i j) = put (i,j)
    get = uncurry I `fmap` get

type Global = Map.Map I Float
data Local  = Local {-# UNPACK #-} !Int !(V.Vector Int)
type WeightsST s = STUArray s I Float
type Weights = UArray I Float



for_ xs f = mapM_ f xs

plus_ :: WeightsST s -> Global -> ST s ()
plus_ w v = do
  for_ (Map.toList v) $ \(i,vi) -> do
              wi <- readArray w i 
              writeArray w i (wi + vi)
minus_ w v = plus_ w (v `scale` (-1))

scale :: Global -> Float -> Global
scale v n = Map.map (*n) v

plus :: Global -> Global -> Global
plus u v = Map.unionWith (+) u v
minus :: Global -> Global -> Global
minus u v = u `plus` (v `scale` (-1))


dot :: Weights -> Local -> Float
{-# INLINE dot #-}
dot w (Local !y x) = V.foldl' (\ !z !i -> z + w ! I y i) 0 x
-- For some reason explicit loop doesn't help here
-- dot !w (Local y x) = go 0 0
--     where !len = V.length x
--           go !z !j | j == len = z
--           go !z !j = go (z + w ! I y (x V.! j)) (j+1)


dot' :: (Float,Weights,Weights) -> Local -> Float
{-# INLINE dot' #-}
-- dot' (!c,!params,!params_a) (Local y x) = V.foldl' (\ !z !j -> 
--                                                     let i   = I y j
--                                                         e   = params   ! i 
--                                                         e_a = params_a ! i
--                                                  in z + (e - (e_a / c)))
--                                             0
--                                             x


dot' (!c,!params,!params_a) (Local y x) = go 0 0
    where !len = V.length x
          go !z !j | j == len = z
          go !z !j = 
              let i   = I y (x V.! j)
                  e   = params   ! i
                  e_a = params_a ! i
              in  go (z + (e - (e_a / c))) (j+1)

toSV :: (V.Unbox Int) => Local -> Global
toSV (Local y v) = Map.fromList [ (I y i,1) | i <- V.toList v ]
